#pragma once
#include <iostream>
#include <chrono>
#include "utility/index.hpp"
#include "request_access/index.hpp"
#include "data_access/index.hpp"
#include "components/index.hpp"
#include "actions.hpp"

using namespace std;
using namespace utility;

namespace components2 {
    class ProductStorage : public components::EntityRequest {
    public:
        COMPONENT_INSTANCE_FUNCTION(ProductStorage);

        ProductStorage();

        any Delete2();

        any InStock();

        any OutStock();

    private:
        bool updateSaleCompleteStatus(const any &saleOrderId, const any &completeOrderId, bool isCancel);
        bool updateSaleInstallStatus(const any &saleOrderId, const any &installOrderId, bool isCancel, bool isInstall);

        string inOutStorage(const EntityData &dict, bool isCancel);

        bool insertProductInStockRecord(const any &completeOrderId, const any& storageId, bool isCancel);
        bool insertProductOutStockRecord(const any &installOrderId, const any& storageId, bool isCancel);
        bool insertMaterialProductOutStockRecord(const any &installOrderId, const any& storageId, bool isCancel);

        bool updateProductInStockNum(const any &completeOrderId, bool isCancel);
        bool updateProductOutStockNum(const any &installOrderId, bool isCancel);
        bool updateMaterialProductOutStockNum(const any &installOrderId, bool isCancel);

        any insert2();

        any inOutStock(int storageType,const string& remarkName, const string& noExistsMsg, const string& orderPrimaryKey);

        entity_access::EntityType *saleOrderEntity;
        entity_access::EntityType *completeOrderEntity;
        entity_access::EntityType *installOrderEntity;
    };

    ProductStorage::ProductStorage() {
        SET_METHOD_ACTION(Delete2);
        SET_METHOD_ACTION(InStock);
        SET_METHOD_ACTION(OutStock);
        saleOrderEntity = entity_access::GetEntityType("SaleOrder");
        completeOrderEntity = entity_access::GetEntityType("ProductCompleteOrder");
        installOrderEntity = entity_access::GetEntityType("InstallOrder");
    }

    std::any ProductStorage::Delete2() {
        lock_guard<std::mutex> lock(mtx);

        EntityData oldEntityData = SelectEntityByPrimaryKey(QueryRequest->PrimaryKeyValue);
        if (oldEntityData.Empty()) Request->GetMessageMap("记录不存在");

        EntityData obj;
        EntityData dict;
        dict["IsDelete"]=1;
        dict["RowVersion"]= Request->GetParameterValue("RowVersion");

        auto msg = CompareVersion(dict, oldEntityData);
        if (!msg.empty()) return Request->GetMessageMap(msg);

        bool blSucceed = true;
        try {
            DataBase->BeginTransaction();

            blSucceed = UpdateEntityByPrimaryKey(QueryRequest->PrimaryKeyValue, dict);

            if(blSucceed) {
                msg = inOutStorage(oldEntityData, true);
                if (!msg.empty()) {
                    obj = Request->GetMessageMap(msg);
                    blSucceed = false;
                }
            }

            if (blSucceed) DataBase->CommitTransaction();
            else DataBase->RollbackTransaction();

            if (obj.Empty())obj = GetSucceedMap(blSucceed);
        } catch (const std::exception &e) {
            DataBase->RollbackTransaction();
            throw MessageException(e.what(),"");
        }

        return obj;
    }

    any ProductStorage::insert2() {
        lock_guard<std::mutex> lock(mtx);

        EntityData obj;
        EntityData dict = Request->RequestEntity.GetEntityData(Entity->Name);

        bool blSucceed = true;
        try {
            DataBase->BeginTransaction();

            blSucceed = InsertEntity(dict);

            if (blSucceed) {
                auto msg = inOutStorage(dict, false);
                if (!msg.empty()) {
                    obj = Request->GetMessageMap(msg);
                    blSucceed = false;
                }
            }

            if (blSucceed) DataBase->CommitTransaction();
            else DataBase->RollbackTransaction();

            if (obj.Empty())obj = GetSucceedMap(blSucceed);
        } catch (const std::exception &e) {
            DataBase->RollbackTransaction();
            throw MessageException(e.what(),"");
        }

        return obj;
    }

    bool ProductStorage::updateSaleCompleteStatus(const any &saleOrderId, const any &completeOrderId, bool isCancel) {
        //先更新产品完成入库状态
        EntityData complete;
        complete["Status"] = isCancel ? 2 : 3;
        bool blSucceed = UpdateEntity(completeOrderEntity, GetQueryByPrimaryKey(completeOrderEntity, completeOrderId), complete);
        if (blSucceed) {
            blSucceed = Actions::UpdateSaleStatus(shared_from_this(), saleOrderId);
            if (blSucceed) blSucceed = Actions::UpdateProduceStatus(shared_from_this(), saleOrderId);
        }
        return blSucceed;
    }

    bool ProductStorage::updateSaleInstallStatus(const any &saleOrderId, const any &installOrderId, bool isCancel, bool isInstall) {
        //先更新提货安装出库状态
        EntityData installOrder;
        installOrder["Status"] = isCancel ? 2 : isInstall ? 3 : 4;
        bool blSucceed = UpdateEntity(installOrderEntity, GetQueryByPrimaryKey(installOrderEntity, installOrderId), installOrder);
        if (blSucceed) {
            blSucceed = Actions::UpdateSaleStatus(shared_from_this(), saleOrderId);
            if (blSucceed) blSucceed = Actions::UpdateProduceStatus(shared_from_this(), saleOrderId);
        }
        return blSucceed;
    }

    string ProductStorage::inOutStorage(const EntityData &dict, bool isCancel) {
        int storageType = dict.GetInt("StorageType");
        bool blSucceed = true;
        string msg;

        auto storageId = dict.Get("StorageId");

        if (storageType == 1) {
            auto saleOrderId = dict.Get("SaleOrderId");
            auto completeOrderId = dict.Get("DataId");
            if (EntityData::IsNull(saleOrderId)) {
                auto completeOrder = SelectEntityByPrimaryKey(completeOrderEntity, completeOrderId);
                if (completeOrder.Empty()) Request->GetMessageMap("产品完成单不存在");
                saleOrderId = completeOrder.Get("SaleOrderId");
            }

            blSucceed = updateSaleCompleteStatus(saleOrderId, completeOrderId, isCancel);
            if (!blSucceed)msg = "更新产品完成状态失败";

            if (blSucceed) {
                blSucceed = insertProductInStockRecord(completeOrderId, storageId, isCancel);
                if (!blSucceed)msg = "添加产品库存记录失败";
            }

            if (blSucceed) {
                blSucceed = updateProductInStockNum(completeOrderId, isCancel);
                if (!blSucceed)msg = "更新产品库存失败";
            }
        } else {
            bool isInstall= dict.GetBool("IsInstall");;
            auto saleOrderId = dict.Get("SaleOrderId");
            auto installOrderId = dict.Get("DataId");
            if (EntityData::IsNull(saleOrderId)) {
                auto installOrder = SelectEntityByPrimaryKey(installOrderEntity, installOrderId);
                if (installOrder.Empty()) Request->GetMessageMap("提货安装单不存在");
                saleOrderId = installOrder.Get("SaleOrderId");
            }

            auto saleOrder = SelectEntityByPrimaryKey(saleOrderEntity, saleOrderId);
            if (saleOrder.Empty()) Request->GetMessageMap("销售单不存在");
            isInstall = saleOrder.GetBool("IsInstall");

            blSucceed = updateSaleInstallStatus(saleOrderId, installOrderId, isCancel,isInstall);
            if (!blSucceed)msg = "更新提货安装状态失败";

            if (blSucceed) {
                blSucceed = insertProductOutStockRecord(installOrderId, storageId, isCancel);
                if (!blSucceed)msg = "添加产品库存记录失败";
            }

            if (blSucceed) {
                blSucceed = updateProductOutStockNum(installOrderId, isCancel);
                if (!blSucceed)msg = "更新产品库存失败";
            }

            if (blSucceed) {
                blSucceed = insertMaterialProductOutStockRecord(installOrderId, storageId, isCancel);
                if (!blSucceed)msg = "添加第三方原料商品库存记录失败";
            }

            if (blSucceed) {
                blSucceed = updateMaterialProductOutStockNum(installOrderId, isCancel);
                if (!blSucceed)msg = "更新第三方原料商品库存失败";
            }
        }

        return msg;
    }

    bool ProductStorage::updateProductInStockNum(const any &completeOrderId, bool isCancel) {
        auto parameters = make_shared<data_access::DataParameters>();
        parameters->Add("@CompleteOrderId", completeOrderId);

        string sql = "update t_ProductAccessory a inner join v_SaleCompleteOrder_Accessory2 b on a.AccessoryId=b.AccessoryId and b.CompleteOrderId=@CompleteOrderId set StockNum=StockNum";
        if (isCancel) sql += "-b.Num";
        else sql += "+b.Num";

        return DataBase->Execute(sql, parameters)>0;
    }

    bool ProductStorage::insertProductInStockRecord(const any &completeOrderId, const any& storageId, bool isCancel) {
        string actionName = isCancel ? "产品完成取消入库" : "产品完成入库";
        auto parameters = make_shared<data_access::DataParameters>();
        parameters->Add("@CompleteOrderId", completeOrderId);
        parameters->Add("@StorageId", storageId);
        parameters->Add("@ActionName", actionName);
        parameters->Add("@CreateUser", Request->OperationUser);

        stringstream sql;
        sql << "insert into t_ProductAccessory_Stock_Record select uuid(),a.AccessoryId,@StorageId,b.StockNum,";
        sql << (isCancel ? "0-a.Num," : "a.Num,");
        sql << (isCancel ? " b.StockNum-a.Num," : " b.StockNum+a.Num,");
        sql << "@ActionName,@CreateUser,CURRENT_TIMESTAMP,uuid() from v_SaleCompleteOrder_Accessory2 a,t_ProductAccessory b";
        sql << " where a.CompleteOrderId=@CompleteOrderId and a.AccessoryId=b.AccessoryId";

        return DataBase->Execute(sql.str(), parameters)>0;
    }

    bool ProductStorage::updateProductOutStockNum(const any &installOrderId, bool isCancel) {
        auto parameters = make_shared<data_access::DataParameters>();
        parameters->Add("@InstallOrderId", installOrderId);

        string sql = "update t_ProductAccessory a inner join v_SaleInstallOrder_Accessory2 b on a.AccessoryId=b.AccessoryId and b.InstallOrderId=@InstallOrderId set StockNum=StockNum";
        if (isCancel) sql += "+b.Num";
        else sql += "-b.Num";

        return DataBase->Execute(sql, parameters)>0;
    }


    any ProductStorage::InStock() {
        return inOutStock(1, "Remark", "产品完成单不存在", "SaleOrderId");
    }

    any ProductStorage::inOutStock(int storageType,const string& remarkName, const string& noExistsMsg, const string& orderPrimaryKey) {
        EntityData dict = Request->RequestEntity.GetEntityData(Entity->Name);
        EntityData entityData;

        entityData["StorageType"] = storageType;
        entityData["DataId"] = QueryRequest->PrimaryKeyValue;
        entityData["StorageDate"] = dict["StorageDate"];
        entityData["StorageEmployeeId"] = dict["StorageEmployeeId"];
        entityData["CreateUser"]= Request->OperationUser;
        entityData["Remark"] = dict[remarkName];

        auto oldEntityData = SelectEntityByPrimaryKey(storageType == 1 ? completeOrderEntity : installOrderEntity, QueryRequest->PrimaryKeyValue);
        if (oldEntityData.Empty()) return Request->GetMessageMap(noExistsMsg);

        auto msg = CompareVersion(dict, oldEntityData);
        if (!msg.empty()) return Request->GetMessageMap(msg);

        entityData[orderPrimaryKey] = oldEntityData[orderPrimaryKey];
        Request->RequestEntity[Entity->Name] = entityData;

        return insert2();
    }

    any ProductStorage::OutStock() {
        return inOutStock(2, "OutRemark", "提货安装单不存在", "SaleOrderId");
    }

    bool ProductStorage::insertProductOutStockRecord(const any &installOrderId, const any &storageId, bool isCancel) {
        string actionName = isCancel ? "提货安装取消出库" : "提货安装出库";
        auto parameters = make_shared<data_access::DataParameters>();
        parameters->Add("@InstallOrderId", installOrderId);
        parameters->Add("@StorageId", storageId);
        parameters->Add("@ActionName", actionName);
        parameters->Add("@CreateUser", Request->OperationUser);

        stringstream sql;
        sql << "insert into t_ProductAccessory_Stock_Record select uuid(),a.AccessoryId,@StorageId,b.StockNum,";
        sql << (isCancel ? "a.Num," : "0-a.Num,");
        sql << (isCancel ? "b.StockNum+a.Num," : "b.StockNum-a.Num,");
        sql << "@ActionName,@CreateUser,CURRENT_TIMESTAMP,uuid() from v_SaleInstallOrder_Accessory2 a,t_ProductAccessory b";
        sql << " where a.InstallOrderId=@InstallOrderId and a.AccessoryId=b.AccessoryId";

        return DataBase->Execute(sql.str(), parameters)>0;
    }

    bool ProductStorage::insertMaterialProductOutStockRecord(const any &installOrderId, const any &storageId, bool isCancel) {
        string actionName = isCancel ? "提货安装取消出库" : "提货安装出库";
        auto parameters = make_shared<data_access::DataParameters>();
        parameters->Add("@InstallOrderId", installOrderId);
        parameters->Add("@StorageId", storageId);
        parameters->Add("@ActionName", actionName);
        parameters->Add("@CreateUser", Request->OperationUser);

        stringstream sql;
        sql << "insert into t_MaterialProduct_Stock_Record select uuid(),a.MaterialProductId,@StorageId,b.StockNum,";
        sql << (isCancel ? "a.Num," : "0-a.Num,");
        sql << (isCancel ? "b.StockNum+a.Num," : "b.StockNum-a.Num,");
        sql << "@ActionName,@CreateUser,CURRENT_TIMESTAMP,uuid() from v_SaleInstallOrder_Material2 a,t_MaterialProduct b";
        sql << " where a.InstallOrderId=@InstallOrderId and a.MaterialProductId=b.MaterialProductId";

        return DataBase->Execute(sql.str(), parameters)>0;
    }

    bool ProductStorage::updateMaterialProductOutStockNum(const any &installOrderId, bool isCancel) {
        auto parameters = make_shared<data_access::DataParameters>();
        parameters->Add("@InstallOrderId", installOrderId);

        string sql = "update t_MaterialProduct a inner join v_SaleInstallOrder_Material2 b on a.MaterialProductId=b.MaterialProductId and b.InstallOrderId=@InstallOrderId set StockNum=StockNum";
        if (isCancel) sql += "+b.Num";
        else sql += "-b.Num";

        return DataBase->Execute(sql, parameters)>0;
    }
}